package com.andox.fn.inf.rpc.rest.util;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

import org.springframework.util.ReflectionUtils;

import com.alibaba.fastjson.JSON;
import com.andox.fn.inf.rpc.rest.config.ReferenceConfig;

public class ReflectUtil {

	public static void main(String[] args) {
		ReferenceConfig sc = new ReferenceConfig();
		sc.setAppName("rpc");
		sc.setAsync(true);
		sc.setBaseUrl("localhost");
		sc.setTimeout(1000);

		Map<String, Object> x = new HashMap<String, Object>();
		findJavaProperties(sc, "service", x);
		System.err.println(JSON.toJSONString(x));
	}

	public static interface InstanceMethodCallback {
		void doWith(Object instance, Method m);
	}

	public static void doWithInstanceMethods(Object object, InstanceMethodCallback callback) {
		ReflectionUtils.doWithMethods(object.getClass(), new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(Method m) throws IllegalArgumentException, IllegalAccessException {
				callback.doWith(object, m);
			}
		});
	}
	
	public static void doWithInstanceMethods(Object object, Class<?> clazz, InstanceMethodCallback callback) {
		ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
			@Override
			public void doWith(Method m) throws IllegalArgumentException, IllegalAccessException {
				callback.doWith(object, m);
			}
		});
	}

	private static boolean isPrimitive(Class<?> type) {
		return type.isPrimitive() || type == String.class || type == Character.class || type == Boolean.class
				|| type == Byte.class || type == Short.class || type == Integer.class || type == Long.class
				|| type == Float.class || type == Double.class || type == Object.class;
	}

	public static void appendConfig(Map<String, String> properties, Object target, Class<?> clazz) {
		doWithInstanceMethods(target, clazz == null ? target.getClass() : clazz, new InstanceMethodCallback() {
			@Override
			public void doWith(Object instance, Method m) {
				String name = m.getName();
				if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())
						&& m.getParameterCount() == 1 && (isPrimitive(m.getParameterTypes()[0])) && (name.startsWith("set"))) {

					String key = null;
					key = m.getName().substring(3, 4).toLowerCase() + m.getName().substring(4);

					try {
						String value = properties.get(key);
						Class<?> x = m.getParameterTypes()[0];
						m.invoke(target, translate(value, x));
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
		});
	}
	
	private static Object translate(String source, Class<?> returnType) {
		if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) {
			return returnType.equals(Boolean.class) ? Boolean.valueOf(source) : Boolean.valueOf(source).booleanValue();
		}else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE)) {
			return returnType.equals(Short.class) ? Short.valueOf(source) : Short.valueOf(source).shortValue();
		}else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE)) {
			return returnType.equals(Integer.class) ? Integer.valueOf(source) : Integer.valueOf(source).intValue();
		}else if (returnType.equals(Long.class) || returnType.equals(Long.TYPE)) {
			return returnType.equals(Long.class) ? Long.valueOf(source) : Long.valueOf(source).longValue();
		}else if (returnType.equals(String.class)) {
			return source;
		}
		
		String error = String.format("Class %s is not support.", returnType.getName());
		throw new RuntimeException(error);
	}
	
	public static void findJavaProperties(Object config, Class<?> clazz, String prefix, Map<String, Object> properties) {
		doWithInstanceMethods(config, clazz == null ? config.getClass() : clazz, new InstanceMethodCallback() {
			@Override
			public void doWith(Object instance, Method m) {
				String name = m.getName();
				if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())
						&& m.getParameterCount() == 0 && (isPrimitive(m.getReturnType())) && (name.startsWith("get")
								|| (name.startsWith("is") && m.getReturnType().equals(Boolean.class)))) {
					String key = null;
					if (m.getName().startsWith("get")) {
						key = m.getName().substring(3, 4).toLowerCase() + m.getName().substring(4);
					} else if (m.getName().startsWith("is")) {
						key = m.getName().substring(2, 3).toLowerCase() + m.getName().substring(3);
					}

					if (prefix != null && prefix.length() != 0)
						key = prefix + "." + key;

					try {
						Object v = m.invoke(config);
						if (v != null)
							properties.put(key, v);
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
		});
	}
	
	public static void findJavaProperties(Object config, String prefix, Map<String, Object> properties) {
		findJavaProperties(config, null, prefix, properties);
	}
	
	public static Object findJavaProperties(Object config, String key) {
		Object[] result = new Object[] {null};
		doWithInstanceMethods(config, new InstanceMethodCallback() {
			@Override
			public void doWith(Object instance, Method m) {
				String name = m.getName();
				if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())
						&& m.getParameterCount() == 0 && (isPrimitive(m.getReturnType())) && (name.startsWith("get") || (name.startsWith("is") && m.getReturnType().equals(Boolean.class)))) {
					String k = null;
					if (m.getName().startsWith("get")) {
						k = m.getName().substring(3, 4).toLowerCase() + m.getName().substring(4);
					} else if (m.getName().startsWith("is")) {
						k = m.getName().substring(2, 3).toLowerCase() + m.getName().substring(3);
					}

					try {
						if (k.equals(key)) {
							Object v = m.invoke(config);
							result[0] = v;
						}
					} catch (Exception e) {
						throw new RuntimeException(e);
					}
				}
			}
		});
		
		return result[0];
	}
	
	public static void appendProperties(Object src, String prefix, Object dest) {
		Map<String, Object> properties = new HashMap<String, Object>();
		findJavaProperties(src, null, properties);
	}
}
